/******************************************************************************
 * tcp_signals.c
 * 
 * Main loop for the bottom half thread.
 * 
 * Copyright (c) 1999-2000, K A Fraser
 * 
 * $Id: tcp_signals.c,v 1.1.1.1 2003/01/19 18:41:58 root Exp $
 */

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/time.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <sys/ioctl.h>
#include <netinet/in.h>

#include "defines.h"

#ifdef USE_PTH
#include "thread.h"
#endif 

#include <asm/types.h>
#include "hash.h"
#include "private.h"

extern char ultcp_cnt;
//#include "lowlevel.h"

#include "libusernet.h"
#ifdef COMPLETE
#include "network_processor_hooks.h"
#else
#include "global_socket.h"
#endif
//#undef printf
//#undef fprintf

#include <stdio.h>

// USE_PTH Added by Manpreet 
#ifdef USE_PTH
pthread_t callback_thread;
#endif

// #define TEST_POLLING
//#define NO_THREADS

#ifdef TEST_POLLING
int num_poll_attempts = 0; 
int num_poll_success = 0; 
int success = 0; 
#endif

// declared in socketcall.c 
extern int fast_gettimeofday(struct timeval *,struct timezone *); 

/* Instead of callback from card, we poll the raw socket - P2 */ 
/******************************************************************************
 * upcall_worker:
 *   Start-point of the upcall worker thread. Sits in a loop, waiting for
 *   callback events, and doing the necessary processing.
 */
void *my_upcall_worker(void *arg)
{
    struct user_pcb *pcb;
    sigset_t         sigs;
#ifdef USE_PTH
    pth_time_t       timeout;
    pth_event_t      ev;
#endif
    u32              work_type;
    int cnt = 0;
    static int count = 0;

#ifdef USE_PTH
#ifdef TEST_POLLING
    pth_time_t start;
    pth_time_t stop;
#endif
#endif

    char *pktptr;
    int pktlength, i;
    int found = 0;
    
    /*    for (i=0;i<ultcp_cnt;i++)
      if (GetCaptureFilter(i)!=NULL){
	found = 1;
	break;
      }
    if (!found){
      printf("Skipping polling. No filter installed yet\n");
      return;
      }*/

#ifndef USE_PTH
    static int first=1; 
    static struct timeval *oldTime; 
    static struct timeval *curTime; 
    double timedif; 
#endif 

#ifndef USE_PTH
    if (first == 1)
      {
	oldTime = (struct timeval *) malloc(sizeof(struct timeval)); 
	curTime = (struct timeval *) malloc(sizeof(struct timeval)); 
	gettimeofday(oldTime, NULL); 
	first = 0; 
      }

    gettimeofday(curTime, NULL); 
    timedif = (curTime->tv_sec - oldTime->tv_sec)* 1000 + (curTime->tv_usec - oldTime->tv_usec)/1000.0; 
    if (timedif > COARSE_TIMEOUT_PERIOD) 
      {
	process_tcp_timers(); 
	gettimeofday(oldTime, NULL); 
      }
#endif 

    //    DB("entered");

    /* Set up our signal callback */
    // usd_init_callback(); - P2

    /*
     * Allows our parent thread to kill us even if we are blocked in the
     * kernel waiting for work to do.
     */
#ifdef USE_PTH
#ifndef NO_THREADS
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
#endif

    fast_gettimeofday(&timeout, NULL);
    timeout.tv_usec += COARSE_TIMEOUT_PERIOD*1000;
    ev = pth_event(PTH_EVENT_TIME, timeout);
#endif

#ifdef USE_PTH
#ifndef NO_THREADS
    /* Signal back to our creator that things are all set up. */
    pthread_mutex_lock(&global_poll_mutex);
    pthread_cond_signal(&global_poll_cond);
    pthread_mutex_unlock(&global_poll_mutex);
#endif
#endif

#ifndef NO_THREADS
    for ( ; ; )
      {
		printf("\nRunning infinite loop..."); 
#endif
	//      printf("\nGoing for sigwait..."); fflush(stdout); 
#ifdef USE_PTH
	pth_sigwait_ev(&sigs, NULL, ev);
#endif
	//      printf("\tReturned from sigwait..."); fflush(stdout); 
	
#ifdef USE_PTH
        if ( pth_event_occurred(ev))
	  {
            process_tcp_timers();
            fast_gettimeofday(&timeout, NULL);
            timeout.tv_usec += COARSE_TIMEOUT_PERIOD*1000;
            pth_event_free(ev, PTH_FREE_THIS);
            ev = pth_event(PTH_EVENT_TIME, timeout);
	  }
#endif

#ifdef USE_PTH
#ifdef NO_THREADS
	pth_event_free(ev, PTH_FREE_THIS);
#endif
#endif

#ifdef TEST_POLLING
	num_poll_attempts++; 
	fast_gettimeofday(&start, NULL); 
#endif
	while ((pktlength=poll_global_socket(&pktptr))) // actually while ("polling returns work")
        {

	  //	  printf("Polling success\n"); fflush(stdout); 

#ifdef TEST_POLLING
	  num_poll_success++; 
	  printf("Polling attempts=%d, success=%d \n", num_poll_attempts, num_poll_success); 
	  fflush(stdout); 
	  success = 1; 
#endif
	  if(pktptr == NULL){
	    fprintf(stderr, "upcall worker: poll global socket retd with null packet. \n");
	    return 1;
	  }
	  
	  rx_skb(pktptr, pktlength); //rx_skb makes its own copy of the packet

	}

#ifdef TEST_POLLING
	if (success == 1)
	  {
	    fast_gettimeofday(&stop, NULL); 
	    //	    printf("\ntime taken = %d sec:%d usec", (stop.tv_sec - start.tv_sec), (stop.tv_usec - start.tv_usec)); 
	    //	    fflush(stdout); 
	  }
	success = 0; 
#endif

#ifndef NO_THREADS
      }
#endif
}


/******************************************************************************
 * Externally called functions
 */

/******************************************************************************
 * init_upcalls:
 *   Initialises the upcall worker thread which deals with callback events.
 */
int init_upcalls()
{
/* We create a worker thread which will perform all protocol processing. */
  printf("\nINIT UPCALLS!\n\n");
#ifdef USE_PTH
if ( pthread_create(&callback_thread, NULL, my_upcall_worker, NULL) )
{
    DB_ERR("couldn't create worker thread: %s", strerror(errno));
    return(0);
}

/* Wait for new thread to finish initialisation. */
 pthread_mutex_lock(&global_poll_mutex);
 pthread_cond_wait(&global_poll_cond, &global_poll_mutex);
 pthread_mutex_unlock(&global_poll_mutex);

 DB("created callback thread (pid=%d)", callback_thread);
#endif
 return(1);
}


/******************************************************************************
 * kill_upcalls:
 *   Kills the upcall worker thread on stack termination.
 */
void kill_upcalls(void)
{
    DB("killing the callback thread");
#ifdef USE_PTH
    pthread_cancel(callback_thread);
    pthread_join(callback_thread, NULL);
    DB("thread dead -- leaving");
#endif
}

